package org.nobject.common.lang;

import static org.nobject.common.lang.ClassUtils.isBoolean;
import static org.nobject.common.lang.ClassUtils.isChar;
import static org.nobject.common.lang.ClassUtils.isCollection;
import static org.nobject.common.lang.ClassUtils.isDate;
import static org.nobject.common.lang.ClassUtils.isMap;
import static org.nobject.common.lang.ClassUtils.isNumber;
import static org.nobject.common.lang.ClassUtils.isString;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.nobject.common.bean.BeanUtils;
import org.nobject.common.exception.AmbiguousException;
import org.nobject.common.exception.ConvertException;
import org.nobject.common.exception.InvokeException;
import org.nobject.common.exception.NoFoundException;
import org.nobject.common.exception.UnSupportException;

/**
 * 对象工具类
 *
 * @author bianrongjun
 * @version 1.0
 */
public class ObjectUtils {
	
	/**
	 * 对象是否在指定数组中
	 * EXP: byContain("1",new String[]{"1","2"})
	 * @param obj 目标对象
	 * @param objs 匹配源
	 */
	public static boolean in(Object obj, Object[] objs) {
		if(objs==null) return false;
		for (int i = 0; i < objs.length; i++) {
			if (obj == objs[i] || obj.equals(objs[i])) return true;
		}
		return false;
	}
	
	/** 
	 * 对象是否在指定数组中
	 * @param obj
	 * @param objs
	 */
	public static boolean in(int obj, int[] objs) {
		for (int i = 0; i < objs.length; i++) {
			if (obj == objs[i] || obj==(objs[i])) return true;
		}
		return false;	
	}
	
	/** 
	 * in
	 * @param obj
	 * @param objs
	 */
	public static boolean in(long obj, long[] objs) {
		for (int i = 0; i < objs.length; i++) {
			if (obj == objs[i] || obj==(objs[i])) return true;
		}
		return false;	
	}
	
	/**
	 * 深度复制
	 * 需要对象序列化
	 * @param srcObj 源对象
	 */
	public static Object deepClone(Object srcObj) {
		Object cloneObj = null;
		try {
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			ObjectOutputStream oo = new ObjectOutputStream(out);
			oo.writeObject(srcObj);

			ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
			ObjectInputStream oi = new ObjectInputStream(in);
			cloneObj = oi.readObject();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return cloneObj;
	}
	
	/******************************************************************************************************************
	 * 
	 *  toObject 区域
	 * 
	 *******************************************************************************************************************/
	
	
	/** 
	 * 转换成对象
	 * @param obj 原对象
	 * @param type 类型
	 * 
	 * @throws ConvertException 转换异常
	 * @throws ClassNotFoundException 类型无法识别异常
	 */
	public static Object toObject(Object obj,String type) throws ConvertException,ClassNotFoundException{
		return toObject(obj,ClassUtils.getClassByNicktype(type));
	}
	
	/** 
	 * toObject
	 * @param obj
	 * @param clazz
	 * @return
	 * @throws ConvertException
	 */
	public static Object toObject(Object obj, Class clazz) throws ConvertException{
		return toObject(obj, clazz, true);
	}
	
	/** 
	 * toObject
	 * @param obj
	 * @param clazz
	 * @param null2def
	 * @return
	 * @throws ConvertException
	 */
	public static Object toObject(Object obj, Class clazz,boolean null2def) throws ConvertException{
		Object o=toObject0(obj, clazz);
		if(o==null&&clazz.isPrimitive()){
			o=defaultValue(clazz);
		}
		return o;
	}
	
	/** 
	 * 将Object转换为指定类型的实例
	 * @param obj 对象
	 * @param clazz 类型
	 * 
	 * @throws ConvertException 转换时异常
	 */
	public static Object toObject0(Object obj, Class clazz) throws ConvertException{
		if(obj==null) return null;
		if(clazz.isAssignableFrom(obj.getClass())) return obj;
		if(clazz.isArray()){
			Class ct=clazz.getComponentType();
			if(obj.getClass().isArray()){
				if(ClassUtils.isEqual(obj.getClass().getComponentType(), clazz.getComponentType())) return obj;
				if(clazz.isPrimitive()) clazz=ClassUtils.getEncapClass(clazz);//自动变为封装类
				int l=Array.getLength(obj);
				Object oo=Array.newInstance(ct,l);
				for(int i=0;i<l;i++) Array.set(oo, i, toObject(Array.get(obj, i),ct));
				return oo;
			}else if(isCollection(obj.getClass())){
				int l=((Collection)obj).size();
				Object oo=Array.newInstance(ct,l);
				Collection objCollection=(Collection)obj;
				int i=0;
				for (Iterator iterator = objCollection.iterator(); iterator.hasNext();) {
					Array.set(oo, i, toObject(iterator.next(),ct));
					i++;
				}
				return oo;
			}else{
				throw new ConvertException("不支持"+ct.getName()+"[]类型由"+obj.getClass().getName()+"的转换");
			}
		}else{
			if(ClassUtils.isEqual(obj.getClass(), clazz)) return obj;
			if(isString(clazz)) 			obj=StringUtils.toString0(obj);
			else if(isNumber(clazz)) 		obj=NumberUtils.toNumber(obj, clazz);
			else if(isBoolean(clazz)) 		obj=BooleanUtils.toBoolean0(obj);
			else if(isDate(clazz))			obj=DateUtils.toDate0(obj);
			else if(isChar(clazz)) 			obj=CharUtils.toChar0(obj);
			else if(isMap(clazz)) 			obj=MapUtils.toMap0(obj);
			else if(isCollection(clazz)) 	obj=CollectionUtils.toCollection0(obj);
			//else if(isClazz(clazz))
			else{
				Method toMethod;
				Constructor c;
				//寻找obj类型中的向指定类型转换的方法
				if((toMethod=ClassUtils.getToMethod(obj.getClass(), Map.class))!=null){
					try {
						return (Map)toMethod.invoke(obj);
					} catch (Exception e) {
						throw new ConvertException(e);
					}
				//寻找指定类型的obj类型构造函数
				}else if((c=ClassUtils.getConstructor(clazz, new Class[]{obj.getClass()}))!=null){
					try {return c.newInstance(obj);} catch (Exception e) {throw new ConvertException("构造函数初始化异常:"+clazz.getName()+"("+obj.getClass().getName()+")."+e.getMessage());}
				//Map注入
				}else if(isMap(obj.getClass())){
					try {return BeanUtils.setBean(clazz.newInstance(),(Map)obj);} catch (Exception e) {throw new ConvertException("不支持Map到该类型的转化"+clazz.getName()+"."+e.getMessage());}
				}else throw new ConvertException("不支持到"+obj.getClass().getName()+"到"+clazz.getName()+"的转换");	
			}
		}
		return obj;
	}
	
	
	
	/******************************************************************************************************************
	 * 
	 *  defaultValue 区域
	 * 
	 *******************************************************************************************************************/
	
	private static final Map<Class<?>, Object> DEFAULTS;

	static {
		Map<Class<?>, Object> map = new HashMap<Class<?>, Object>();
		put(map, boolean.class, false);
		put(map, char.class, '\0');
		put(map, byte.class, (byte) 0);
		put(map, short.class, (short) 0);
		put(map, int.class, 0);
		put(map, long.class, 0L);
		put(map, float.class, 0f);
		put(map, double.class, 0d);
		DEFAULTS = Collections.unmodifiableMap(map);
	}
	
	private static <T> void put(Map<Class<?>, Object> map, Class<T> type, T value) {
	  map.put(type, value);
	}

	/** 
	 * 获取默认值
	 * @param type
	 */
	public static <T> T defaultValue(Class<T> type) {
	  return (T) DEFAULTS.get(type);
	}
	
	/******************************************************************************************************************
	 * 
	 *  toObjects 区域
	 * 
	 *******************************************************************************************************************/
	
	/** 
	 * 转换成指定类型的实例数组
	 * @param objs 实例数组
	 * @param clazz 类型
	 * @throws ConvertException
	 */
	public static Object[] toObjects(Object[] objs,Class clazz) throws ConvertException{
		for (int i = 0; i < objs.length; i++) objs[i]=toObject(objs[i], clazz);
		return objs;
	}
	
	/** 
	 * 转换成指定类型数组的实例数组
	 * @param objs 实例数组
	 * @param clazzes 类型数组
	 * @return
	 * @throws UnSupportException
	 */
	public static Object[] toObjects(Object[] objs,Class[] clazzes) throws ConvertException{
		for (int i = 0; i < objs.length; i++) objs[i]=toObject(objs[i], clazzes[i]);
		return objs;
	}
	
	/** 
	 * Collection转成Object数组
	 * @param collection Collection
	 */
	public static Object[] toObjects(Collection collection){
		return collection.toArray();
	}
	
	/** 
	 * Collection转换为Object数组
	 * @param collection
	 * @param clazz
	 * @throws 转换时错误 
	 */
	public static Object[] toObjects(Collection collection,Class clazz) throws ConvertException{
		return toObjects(collection.toArray(), clazz);
	}
	
	/** 
	 * 获取对象级联对象
	 * @param o 对象
	 * @param key 键值
	 * @throws NoFoundException
	 * @throws AmbiguousException
	 * @throws InvokeException
	 */
	public static Object fetch(Object o,String key)throws NoFoundException,AmbiguousException,InvokeException{
		String[] ks=key.split("\\.");
		Object io=o;
		for(String k:ks){
			Class c=io.getClass();
			if(ClassUtils.isMap(c)){
				io=((Map)io).get(k);
			}else if(ClassUtils.isCollection(c)){
				io=CollectionUtils.get((Collection)io, Integer.valueOf(k));
			}else{
				io=BeanUtils.getProperty(io, k);
			}
		}
		return io;
	}
	
	/** 
	 * 是否相等
	 * @param o1
	 * @param o2
	 */
	public static boolean equal(Object o1,Object o2){
		if(o1==o2) return true;
		if(o1!=null) return o1.equals(o2);
		return false;
	}
	
	/** 
	 * equal0
	 * @param o1
	 * @param o2
	 * @return
	 */
	public static boolean same(Object o1,Object o2){
		Class c1=o1.getClass();
		Class c2=o2.getClass();
		if(ClassUtils.isNumber(c1)&& ClassUtils.isNumber(c2)){
			return StringUtils.toString0(o1).equals(StringUtils.toString0(o2));
		}
		return o1.equals(o2);
	}
	
	
	/** 
	 * 是否为空
	 * @param o 数据
	 */
	public static boolean isEpmty(Object o){
		if(o==null) return true;
		if(o instanceof String) return StringUtils.isEmpty((String)o);
		return false;
	}
	
	/** 
	 * NULL
	 * @param o
	 * @param def
	 */
	public static Object n2d(Object o,Object def){
		return o==null?def:o;
	}
	
	/** 
	 * 返回一个非空的对象
	 * @param os
	 */
	public static Object unnull(Object... os){
		for(Object o:os){
			if(o!=null) return o;
		}
		return null;
	}
	
	public static boolean isEmpty(Object o){
		if(o==null) return true;
		Class c=o.getClass();
		if(c.isArray()){
			return ArrayUtils.isEmpty0(o);
		}else{
					if(ClassUtils.isString(c))		{return StringUtils.isEmpty((String)o);}
			else 	if(ClassUtils.isMap(c))			{return MapUtils.isEmpty((Map)o);}
			else 	if(ClassUtils.isCollection(c)) {return CollectionUtils.isEmpty((Collection)o);}
			else{
				return o==null;
			}
		}
	}
	
	/** 
	 * 对象比较
	 * @param o1
	 * @param o2
	 */
	public static int compare(Object o1,Object o2) {
		if(o1==null&&o2!=null) 		return -1;
		else if(o1!=null&&o2==null) return 1;
		else if(o1==null&&o2==null) return 0;
		else if(o1.equals(o2)) return 0;
		
		Class c1=o1.getClass();
		if(ClassUtils.isString(c1)){
			return ((String)o1).compareTo(((String)o2));
		}else if(ClassUtils.isNumber(c1)){
			if(ClassUtils.isInt(c1)) 		return (Integer)o1-(Integer)o2;
			else if(ClassUtils.isLong(c1)) 	{long 	l=(Long)o1	-(Long)o2;			return l==0?0:(l>0?1:-1);}
			else if(ClassUtils.isDouble(c1)){double l=(Double)o1-(Double)o2;		return l==0?0:(l>0?1:-1);}
			else if(ClassUtils.isFloat(c1)) {float 	l=(Float)o1	-(Float)o2;			return l==0?0:(l>0?1:-1);}
			else if(ClassUtils.isShort(c1)) {short 	l=(short)((Short)o1	-(Short)o2);return l==0?0:(l>0?1:-1);}
			else{
				try {
					return NumberUtils.toInt(o1)-NumberUtils.toInt(o2);
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			}
		}else if(ClassUtils.isChar(c1)){
			return (Character)o1-(Character)o2;
		}else if(ClassUtils.isByte(c1)){
			return (Byte)o1-(Byte)o2;
		}else{
			throw new RuntimeException("无法比较"+c1.getName());
		}
		
	}
	
	public static void main(String[] args) throws Exception{
		Object obj=new int[]{3};
		Object o=toObject(obj, String[].class);
		System.out.println(o.getClass().getName()+":"+o);
	}
}
